import { Injectable } from '@angular/core';
import {G, SVG, Svg, Text} from '@svgdotjs/svg.js';
import {EnaTerminalList, EnaTerminalList2, VirtualLoopData} from '../../models/svgdata';
import {drawConfigType, drawPropsType, loopBranchType} from '../types/draw-config.type';


@Injectable({
  providedIn: 'root'
})
export class DrawGraphService {

  private branchVisibilityMap = new Map<string, boolean>()

  private drawDefaultConfig: drawConfigType = {
    baseDrawConfig: {
      moduleDistance: 700,
      font: {
        small: 8,
        normal: 11,
      },
      color: {
        primary: '#00367a',
        moduleBorderColor: '#27cef7',
        accent: '#25ccf7',
        text: '#ffffff',
        dashedLine: '#000000',
      }
    },
    loopBranchConfig: {
      lineColor: '#25ccf7',
      textColor: 'white',
      textSize: 8,
      lineDistance: 700,
    }
  }

  // 预估高度计算
  public heightCompute(data: VirtualLoopData): number {
    let predictHeight = 100
    data.connectionData[0].outputList.forEach(output => {
      output.enaTerminalList.forEach(enTerminal => {
        predictHeight += 20 * enTerminal.terminalList.length
      })
      predictHeight += 60
    })
    data.connectionData[0].inputList.forEach(input => {
      input.enaTerminalList.forEach(enTerminal => {
        predictHeight += 20 * enTerminal.terminalList.length
      })
      predictHeight += 60
    })
    predictHeight -= 20
    return predictHeight
  }

  public render(draw: G, data: VirtualLoopData, drawPropsType: drawPropsType ) {
    const drawConfig = drawPropsType.drawConfig || this.drawDefaultConfig;
    const group = draw.group()
    const leftGroup = group.group()
    const rightGroup = group.group()

    let elementHeight = 25  //预估模块元素渲染高度,从上向下依次绘制

    const leftGroupRect = leftGroup
      .rect(drawPropsType.moduleSize.width,drawPropsType.moduleSize.height)
      .fill(drawConfig.baseDrawConfig.color.primary)
      .stroke({ width: 0.5, color: drawConfig.baseDrawConfig.color.moduleBorderColor });

    leftGroup.text(data.connectionData[0].noCentreIed.name)
      .fill(drawConfig.baseDrawConfig.color.text)
      .font({ size: drawConfig.baseDrawConfig.font.normal})
      .center(leftGroupRect.cx(), elementHeight);

    const rightGroupRect = rightGroup
      .rect(drawPropsType.moduleSize.width,drawPropsType.moduleSize.height)
      .fill(drawConfig.baseDrawConfig.color.primary)
      .stroke({ width: 0.5, color: drawConfig.baseDrawConfig.color.moduleBorderColor });


    rightGroup.rect(drawPropsType.moduleSize.width + 10,drawPropsType.moduleSize.height + 10)
      .move(-5, -5).fill('none')
      .stroke({width: 0.5, color: drawConfig.baseDrawConfig.color.moduleBorderColor, dasharray: '5,5'});  //虚线框


    rightGroup.text(data.centreIed.name)
      .fill(drawConfig.baseDrawConfig.color.text)
      .font({ size: drawConfig.baseDrawConfig.font.normal })
      .center(rightGroupRect.cx(), elementHeight);

    elementHeight += 15;
    this.renderMultilineText(leftGroup, data.connectionData[0].noCentreIed.desc, leftGroupRect.cx(), elementHeight);
    this.renderMultilineText(rightGroup, data.centreIed.desc, rightGroupRect.cx(), elementHeight);

    //--------------------------以下为依赖数据变化模块元素-----------------------------------

    for(let outputItem of data.connectionData[0].outputList){
      const branchId = `output-${outputItem.id}`;
      this.branchVisibilityMap.set(branchId, true);
      elementHeight += 60;
      leftGroup.circle(16).fill(drawConfig.baseDrawConfig.color.accent).center(70, elementHeight);

      leftGroup.text(outputItem.outputPort)
        .fill(drawConfig.baseDrawConfig.color.text)
        .font({ size: drawConfig.baseDrawConfig.font.small, weight: 'bold' })
        .move(drawPropsType.moduleSize.width-2, elementHeight-11)
        .attr('text-anchor', 'end');

      const rightMessageText = rightGroup.rect(60,20)
        .fill(drawConfig.baseDrawConfig.color.accent)
        .move(drawPropsType.moduleSize.width-60-10, elementHeight-20/2);

      rightGroup.text('GOOSE '+ outputItem.appid16)
        .font({ size: drawConfig.baseDrawConfig.font.small })
        .center(rightMessageText.cx(), rightMessageText.cy());

      rightGroup.text(outputItem.inputPort)
        .fill(drawConfig.baseDrawConfig.color.text)
        .font({ size: drawConfig.baseDrawConfig.font.small, weight: 'bold' })
        .move(2, elementHeight-11);


      group.line(70, elementHeight, drawPropsType.moduleSize.width+drawConfig.baseDrawConfig.moduleDistance-70+5, elementHeight)
        .stroke({ width: 2, color: drawConfig.baseDrawConfig.color.accent });

      this.renderArrowHead(group, drawPropsType.moduleSize.width+180, elementHeight, branchId)
        .rotate(180, drawPropsType.moduleSize.width+180, elementHeight);

      this.renderOpenOrCloseMark(leftGroup, this.branchVisibilityMap.get(branchId) as Boolean, 70+30, elementHeight - 11, branchId);

      elementHeight = this.renderLoopBranch(group, elementHeight, outputItem.enaTerminalList, {x: 70+22, y: elementHeight}, {x: drawPropsType.moduleSize.width+drawConfig.baseDrawConfig.moduleDistance-70+5-22, y: elementHeight},drawConfig.loopBranchConfig, branchId);
    }

    for(let inputItem of data.connectionData[0].inputList){
      const branchId = `input-${inputItem.id}`;
      this.branchVisibilityMap.set(branchId, true);
      elementHeight += 45
      const leftMessageText = leftGroup.rect(60,20)
        .fill(drawConfig.baseDrawConfig.color.accent)
        .move(10, elementHeight-20/2);

      leftGroup.text('GOOSE '+ inputItem.appid16)
        .font({ size: drawConfig.baseDrawConfig.font.small })
        .center(leftMessageText.cx(), leftMessageText.cy());

      leftGroup.text(inputItem.inputPort)
        .fill(drawConfig.baseDrawConfig.color.text)
        .font({ size: drawConfig.baseDrawConfig.font.small, weight: 'bold' })
        .move(drawPropsType.moduleSize.width-2, elementHeight-11)
        .attr('text-anchor', 'end');

      rightGroup.circle(16)
        .fill(drawConfig.baseDrawConfig.color.accent)
        .center(drawPropsType.moduleSize.width-70,elementHeight);

      rightGroup.text(inputItem.outputPort)
        .fill(drawConfig.baseDrawConfig.color.text)
        .font({ size: drawConfig.baseDrawConfig.font.small, weight: 'bold' })
        .move(4, elementHeight-15);

      group.line(70, elementHeight, drawPropsType.moduleSize.width+drawConfig.baseDrawConfig.moduleDistance-70+5, elementHeight)
        .stroke({ width: 2, color: drawConfig.baseDrawConfig.color.accent });

      this.renderArrowHead(group, drawConfig.baseDrawConfig.moduleDistance-180, elementHeight, branchId);
      this.renderOpenOrCloseMark(leftGroup, this.branchVisibilityMap.get(branchId) as Boolean, 70+30, elementHeight - 11, branchId);

      this.renderLoopBranch(group, elementHeight, inputItem.enaTerminalList, {x: 70+22, y: elementHeight}, {x: drawPropsType.moduleSize.width+drawConfig.baseDrawConfig.moduleDistance-70+5-22, y: elementHeight},drawConfig.loopBranchConfig, branchId);

    }

    rightGroup.move(drawConfig.baseDrawConfig.moduleDistance,-5)

    group.center(draw.cx(), draw.cy())

  }

  // 多文本换行渲染
  public renderMultilineText(group: G, text: string, centerX: number, centerY: number, maxWords: number = 19) {
    const words = text.split('');
    let currentLine = '';
    let lines = [];

    for (let i = 0; i < words.length; i++) {
      if (currentLine.length >= maxWords) {
        lines.push(currentLine);
        currentLine = words[i];
      } else {
        currentLine += words[i];
      }
    }

    if (currentLine) lines.push(currentLine);
    lines.forEach((line, index) => {
      group.text(line).fill('#ffffff').font({ size: 11 }).center(centerX, centerY + index * 14)
    });
  }

  // 主线分支渲染
  public renderLoopBranch(group: G, elementHeight: number, enaTerminalListData: EnaTerminalList[] | EnaTerminalList2[], leftPoint: {x: number, y: number}, rightPoint: {x: number, y: number}, loopBranch: loopBranchType, branchId?: string): number {
    const branchGroup = group.group()
    if(branchId){
      branchGroup.id(branchId)
    }
    let height = 20
    for(let item of enaTerminalListData) {
      const hasSwitch = !!item.ena
      if(item.terminalList.length > 1){
        branchGroup.path(`M${rightPoint.x} ${rightPoint.y} l0 ${height + 20 * (item.terminalList.length - 1)/2} l-152 0`).stroke({ width: 0.5, color: loopBranch.lineColor }).fill('none')
        if(hasSwitch){
          this.renderSoftStrap(branchGroup, loopBranch.lineDistance+14, height + 20 * (item.terminalList.length - 1)/2 + elementHeight-4,  item.ena.outputEnaDesc)
        }
        const endX = rightPoint.x - 152
        const endY = rightPoint.y + height+ 20 * (item.terminalList.length - 1)/2
        for(let branch of item.terminalList) {
          branchGroup.path(`M${endX} ${endY} l0 ${rightPoint.y + height - endY} l-572 0 l0 -20`).stroke({ width: 0.5, color: loopBranch.lineColor }).fill('none')
          branchGroup.text(hasSwitch ? item.terminalList[0].inputDesc : item.terminalList[0].outputDesc).fill(loopBranch.textColor).font({size: loopBranch.textSize}).move(250, height + rightPoint.y-11)
          branchGroup.text(hasSwitch ? item.terminalList[0].outputDesc : item.terminalList[0].inputDesc).fill(loopBranch.textColor).font({size: loopBranch.textSize}).move(670, height + rightPoint.y-11).attr('text-anchor', 'end')
          height += 20
        }
      }else if(item.terminalList.length === 1){
        branchGroup.path(`M${rightPoint.x} ${rightPoint.y} l0 ${height} l-724 0 l0 -20`).stroke({ width: 0.5, color: loopBranch.lineColor }).fill('none')
        if(hasSwitch){
          this.renderSoftStrap(branchGroup, loopBranch.lineDistance+14, height + elementHeight-4, item.ena.outputEnaDesc)
        }
        branchGroup.text(hasSwitch ? item.terminalList[0].inputDesc : item.terminalList[0].outputDesc).fill(loopBranch.textColor).font({size: loopBranch.textSize}).move(250, height + rightPoint.y-11)
        branchGroup.text(hasSwitch ? item.terminalList[0].outputDesc : item.terminalList[0].inputDesc).fill(loopBranch.textColor).font({size: loopBranch.textSize}).move(670, height + rightPoint.y-11).attr('text-anchor', 'end')
        height += 20
      }
    }
    return elementHeight + height - 20
  }

  // 软压板渲染
  public renderSoftStrap(group: G, x: number, y: number, desc: string){
    const softStrapGroup = group.group()

    softStrapGroup.rect(110, 24).fill('transparent').stroke('none')

    const buttonGroup = softStrapGroup.group()
    buttonGroup.rect(24, 8).radius(4).fill('#bdf4f1')
    buttonGroup.circle(8).cx(4).cy(4).fill('white')
    buttonGroup.circle(8).cx(20).cy(4).fill('white')

    buttonGroup.center(55, 4)

    softStrapGroup.text(desc).fill('#ffffff').font({size: 8}).center(55, 13)

    softStrapGroup.move(x, y)
  }

  // 箭头渲染
  public renderArrowHead(group: G, x: number, y: number, branchId?: string, size: number = 8, color: string = '#25ccf7'): G {
    const arrowGroup = group.group()
    const pathData = `M${x} ${y} l-${size * 2} -${size} l${size} ${size} l-${size} ${size} Z`

    arrowGroup.path(pathData).fill(color)
    arrowGroup.attr({ cursor: 'pointer' })
    arrowGroup.attr({ 'pointer-events': 'bounding-box' })
    arrowGroup.on('click', () => {
      if (branchId){
        const isOpen = this.branchVisibilityMap.get(branchId)
        isOpen ? SVG(`#${branchId}`).hide() : SVG(`#${branchId}`).show()
        this.branchVisibilityMap.set(branchId, !isOpen)
        const textElement = group.findOne(`#isOpen-${branchId}`) as Text
        textElement.text(!isOpen ? '展开' : '收缩')
      }
    })
    return arrowGroup
  }

  public renderOpenOrCloseMark(group: G, open: Boolean, x: number, y: number, branchId: string){
    const groupRect = group.rect(20, 10).fill('#0985d9').move(x,y)
    group.text(open ? '展开' : '收缩').font({ size: 8, weight: 'bold' }).center(groupRect.cx(), groupRect.cy()).id(`isOpen-${branchId}`)
  }
}
